namespace Guia.Elements
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Windows.Automation;
    using Guia.Core;
    using Guia.Elements.PatternServants;
    using Guia.Locator;

    /// <summary>
    /// This class represents a ComboBox
    /// </summary>
    public class ComboBox : UiElement, IExpandCollapse
    {
        /// <summary>
        /// Represents a helper-servant for the ExpandCollapse-pattern.
        /// </summary>
        private readonly IExpandCollapse expandCollapseServant;
        
        /// <summary>
        /// Initializes a new instance of the ComboBox class. 
        /// </summary>
        /// <param name="automationElement">The AutomationElement representating the ComboBox.</param>
        public ComboBox(AutomationElement automationElement)
            : base(automationElement)
        {
            this.expandCollapseServant = new ExpandCollapseImpl(this);
        }

        /// <summary>
        /// Gets the default-condition to find a control by its type.
        /// </summary>
        public static SearchCondition ControlTypeCondition
        {
            get { return SearchCondition.ByControlType(ControlType.ComboBox); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the state of this ComboBox is expanded or not.
        /// </summary>
        public bool IsExpanded
        {
            get
            {
                return this.expandCollapseServant.IsExpanded;
            }

            set
            {
                this.expandCollapseServant.IsExpanded = value;
            }
        }

        /// <summary>
        /// Gets the selected item of the ComboBox. Returns null if no item is selected
        /// </summary>
        public ComboBoxItem SelectedItem
        {
            get
            {
                ComboBoxItem[] items = this.GetAll<ComboBoxItem>();
                ComboBoxItem selectedItem = null;
                foreach (ComboBoxItem item in items)
                {
                    if (item.IsSelected)
                    {
                        selectedItem = item;
                        break;
                    }
                }

                return selectedItem;
            }
        }

        /// <summary>
        /// Gets all items of this ComboBox
        /// </summary>
        public ComboBoxItem[] Items
        {
            get
            {
                return this.GetAll<ComboBoxItem>();
            }
        }

        /// <summary>
        /// Returns the ComboBoxItem with the given TextValue.
        /// </summary>
        /// <param name="value">
        /// >The TextValue of the Item.
        /// </param>
        /// <exception cref="ArgumentNullException">This exception is thrown if value is null</exception>
        public ComboBoxItem this[string value]
        {
            get
            {
                Contract.Requires<ArgumentNullException>(value != null);
                return this.GetItem(value);
            }
        }

        /// <summary>
        /// Gets the ComboBoxItem at the given position.
        /// </summary>
        /// <param name="index">The index of the desired ComboBoxItem.</param>
        /// <returns>The ComboBoxItem at the given position.</returns>
        /// <exception cref="ElementNotFoundException">This exception is thrown if no ComboBoxItem exist at the given position.</exception>
        /// <exception cref="ArgumentOutOfRangeException">This exception is thrown if index is not within the valid range</exception>
        public ComboBoxItem this[int index]
        {
            get
            {
                Contract.Requires<ArgumentOutOfRangeException>(index >= 0 && index < this.Items.Length);
                return this.GetItem(index);
            }
        }

        /// <summary>
        /// Expands the ComboBox
        /// </summary>
        public void Expand()
        {
            this.expandCollapseServant.Expand();
        }

        /// <summary>
        /// Collapses the ComboBox
        /// </summary>
        public void Collapse()
        {
            this.expandCollapseServant.Collapse();
        }

        /// <summary>
        /// Return whether this ComboBox contains an item with this value or not
        /// </summary>
        /// <param name="value">The TextValue of the ComboBoxItem</param>
        /// <returns>True if the ComboBox contains such an item. False otherwise</returns>
        /// <exception cref="ArgumentNullException">This exception is thrown if value is null</exception>
        public bool Contains(string value)
        {
            Contract.Requires<ArgumentNullException>(value != null);
            bool contains = false;
            try
            {
                ComboBoxItem item = this.Get<ComboBoxItem>(SearchCondition.ByName(value));
                if (item != null)
                {
                    contains = true;
                }
            }
            catch (ElementNotFoundException)
            {
            }

            return contains;
        }
        
        /// <summary>
        /// Returns the ComboBoxItem with the given TextValue.
        /// </summary>
        /// <param name="value">The TextValue of the Item.</param>
        /// <returns>The first Item with the given TextValue.</returns>
        /// <exception cref="ArgumentNullException">This exception is thrown if value is null</exception>
        public ComboBoxItem GetItem(string value)
        {
            Contract.Requires<ArgumentNullException>(value != null);
            ComboBoxItem item = this.Get<ComboBoxItem>(SearchCondition.ByName(value));
            if (!item.IsEnabled)
            {
                this.Expand();
            }

            return item;
        }

        /// <summary>
        /// Gets the ComboBoxItem at the given position.
        /// </summary>
        /// <param name="index">The index of the desired ComboBoxItem.</param>
        /// <returns>The ComboBoxItem at the given position.</returns>
        /// <exception cref="ElementNotFoundException">This exception is thrown if no ComboBoxItem exist at the given position.</exception>
        /// <exception cref="ArgumentOutOfRangeException">This exception is thrown if index is not within the valid range</exception>
        public ComboBoxItem GetItem(int index)
        {
            Contract.Requires<ArgumentOutOfRangeException>(index >= 0 && index < this.Items.Length);
            try
            {
                ComboBoxItem[] items = this.Items;
                ComboBoxItem item = items[index];
                if (!item.IsEnabled)
                {
                    this.Expand();
                }

                return item;    
            } 
            catch (IndexOutOfRangeException)
            {
                throw new ElementNotFoundException("No combobox-item found with the index " + index);
            }
        }
    }
}